#pragma once



#include <vector>
#include <string>
#include <map>
#include <memory>
#include <stdint.h>
#include <atomic>
#include "common/AnyValue.h"

enum class EZValueType{
	nullValue = 0,
	int64Value,
	uint64Value,
	realValue,
	stringValue,
	booleanValue,
	arrayValue,
	objectValue
};

typedef std::vector<std::string> Members;

class EZValue
{
	typedef std::vector<EZValue>			ArrValue;
	typedef std::map<std::string, EZValue>	ObjValue;
public:
	EZValue(EZValueType type = EZValueType::nullValue);
	EZValue(int8_t v){
		valueType_ = EZValueType::int64Value;
		value_ = (int64_t)v;
	}
	EZValue(uint8_t v){
		valueType_ = EZValueType::uint64Value;
		value_ = (uint64_t)v;
	}
	EZValue(int16_t v){
		valueType_ = EZValueType::int64Value;
		value_ = (int64_t)v;
	}
	EZValue(uint16_t v){
		valueType_ = EZValueType::uint64Value;
		value_ = (uint64_t)v;
	}
	EZValue(int32_t v){
		valueType_ = EZValueType::int64Value;
		value_ = (int64_t)v;
	}
	EZValue(uint32_t v){
		valueType_ = EZValueType::uint64Value;
		value_ = (uint64_t)v;
	}
	EZValue(int64_t v){
		valueType_ = EZValueType::int64Value;
		value_ = v;
	}
	EZValue(uint64_t v){
		valueType_ = EZValueType::uint64Value;
		value_ = v;
	}
    EZValue(float v) {
        valueType_ = EZValueType::realValue;
        value_ = (double)v;
    }
	EZValue(double v){
		valueType_ = EZValueType::realValue;
		value_ = v;
	}
	EZValue(bool v){
		valueType_ = EZValueType::booleanValue;
		value_ = v;
	}
	EZValue(const char* v){
		valueType_ = EZValueType::stringValue;
		value_ = std::string(v);
	}
	EZValue(const std::string& v){
		valueType_ = EZValueType::stringValue;
		value_ = v;
	}


	//////////////////////////////////////////////////////////////////////////
	std::string asString() const;
	int64_t asInt64() const;
	uint64_t asUInt64() const;
	double  asDouble() const;
	bool	asBool() const;

	//////////////////////////////////////////////////////////////////////////
	EZValueType type() const{
		return valueType_;
	}
	bool isNull() const{
		return valueType_ == EZValueType::nullValue;
	}
	bool isIntegral() const{
		return valueType_ == EZValueType::int64Value
			|| valueType_ == EZValueType::uint64Value
			|| valueType_ == EZValueType::booleanValue;
	}
	bool isReal() const{
		return valueType_ == EZValueType::realValue;
	}
	bool isBool() const{
		return valueType_ == EZValueType::booleanValue;
	}
	bool isString() const{
		return valueType_ == EZValueType::stringValue;
	}
	bool isObject() const{
		return valueType_ == EZValueType::objectValue;
	}
	bool isArray() const{
		return valueType_ == EZValueType::arrayValue;
	}

	//////////////////////////////////////////////////////////////////////////
	size_t size() const;

	bool empty() const
    {
		if (isArray() || isObject())
			return size() == 0u;
		return false;
	}

	//////////////////////////////////////////////////////////////////////////
	void clear()
    {
		if (isArray()){
			value_.get<ArrValue>().clear();
		}else if (isObject()){
			value_.get<ObjValue>().clear();
		}
	}

	EZValue removeMember(const std::string& key);

	bool isMember(const std::string& key) const;

	//////////////////////////////////////////////////////////////////////////
	EZValue &operator[](size_t index);
	const EZValue &operator[](size_t index) const;
	EZValue &operator[](const std::string key);
	const EZValue &operator[](const std::string key) const;

	//////////////////////////////////////////////////////////////////////////
	Members memberNames() const;

private:
	static EZValue& null_value() {
		static EZValue null_v;
		return null_v;
	}
private:
	EZValueType valueType_;
	AnyValue    value_;
    
};


inline EZValue::EZValue(EZValueType type /*= EZValueType::nullValue*/)
{
	valueType_ = type;
	switch (type)
	{
	case EZValueType::nullValue:
		break;
	case EZValueType::int64Value:
		value_ = (int64_t)0;
		break;
	case EZValueType::uint64Value:
		value_ = (uint64_t)0;
		break;
	case EZValueType::realValue:
		value_ = 0.0;
		break;
	case EZValueType::stringValue:
		value_ = std::string();
		break;
	case EZValueType::booleanValue:
		value_ = false;
		break;
	case EZValueType::arrayValue:
		value_ = ArrValue();
		break;
	case EZValueType::objectValue:
		value_ = ObjValue();
		break;
	default:
		break;
	}
}


inline std::string EZValue::asString() const
{
	switch (valueType_)
	{
	case EZValueType::nullValue:
		return "";
	case EZValueType::stringValue:
		return value_.get<std::string>();
	case EZValueType::booleanValue:
		return value_.get<bool>() ? "true" : "false";
	case EZValueType::int64Value:
		return std::to_string(value_.get<int64_t>());
	case EZValueType::uint64Value:
		return std::to_string(value_.get<uint64_t>());
	case EZValueType::realValue:
		return std::to_string(value_.get<double>());
	default:
		throw std::runtime_error("value type error");
	}
	return "";
}

inline int64_t EZValue::asInt64() const
{
	switch (valueType_)
	{
	case EZValueType::nullValue:
		return 0;
	case EZValueType::int64Value:
		return value_.get<int64_t>();
	case EZValueType::uint64Value:
		return value_.get<uint64_t>();
	case EZValueType::realValue:
		return int64_t(value_.get<double>());
	case EZValueType::booleanValue:
		return value_.get<bool>() ? 1 : 0;
    case EZValueType::stringValue:
        return std::stoll(value_.get<std::string>());
	default:
		throw std::runtime_error("value type error");
	}
	return 0;
}

inline uint64_t EZValue::asUInt64() const
{
	switch (valueType_)
	{
	case EZValueType::nullValue:
		return 0;
	case EZValueType::int64Value:
		return value_.get<int64_t>();
	case EZValueType::uint64Value:
		return value_.get<uint64_t>();
	case EZValueType::realValue:
		return uint64_t(value_.get<double>());
	case EZValueType::booleanValue:
		return value_.get<bool>() ? 1 : 0;
    case EZValueType::stringValue:
        return std::stoull(value_.get<std::string>());
	default:
		throw std::runtime_error("value type error");
	}
	return 0;
}

inline double EZValue::asDouble() const
{
	switch (valueType_)
	{
	case EZValueType::nullValue:
		return 0.;
	case EZValueType::int64Value:
		return double(value_.get<int64_t>());
	case EZValueType::uint64Value:
		return double(value_.get<uint64_t>());
	case EZValueType::realValue:
		return value_.get<double>();
	case EZValueType::booleanValue:
		return value_.get<bool>() ? 1. : 0.;
    case EZValueType::stringValue:
        return std::stod(value_.get<std::string>());
	default:
		throw std::runtime_error("value type error");
	}
	return 0.;
}

inline bool EZValue::asBool() const
{
	switch (valueType_)
	{
	case EZValueType::nullValue:
		return false;
	case EZValueType::int64Value:
		return value_.get<int64_t>() != 0;
	case EZValueType::uint64Value:
		return value_.get<uint64_t>() != 0;
	case EZValueType::realValue:
		return value_.get<double>() != 0.;
	case EZValueType::booleanValue:
		return value_.get<bool>();
    case EZValueType::stringValue:
        if (value_.get<std::string>() == "true" || value_.get<std::string>() == "TRUE") {
            return true;
        }else if (value_.get<std::string>() == "false" || value_.get<std::string>() == "FALSE")
        {
            return false;
        }
        throw std::runtime_error("Cannot be converted Because it's not \"true\" or \"false\"");
	default:
		throw std::runtime_error("value type error");
	}
	return false;
}

inline size_t EZValue::size() const
{
	switch (valueType_)
	{
	case EZValueType::arrayValue:
		return value_.get<ArrValue>().size();
	case EZValueType::objectValue:
		return value_.get<ObjValue>().size();
	default:
		break;
	}
	return 0;
}

inline EZValue EZValue::removeMember(const std::string& key)
{
	if (valueType_ != EZValueType::nullValue && valueType_ != EZValueType::objectValue)
		return null_value();
	if (valueType_ == EZValueType::nullValue)
		return null_value();

	auto it = value_.get<ObjValue>().find(key);
	if (it == value_.get<ObjValue>().end())
		return null_value();

	EZValue old(value_.get<ObjValue>()[key]);
	value_.get<ObjValue>().erase(it);
	return old;
}

inline bool EZValue::isMember(const std::string& key) const
{
	if (valueType_ != EZValueType::objectValue){
		return false;
	}
	const EZValue& v = ((*this)[key]);
	return &v != &null_value();
}

inline EZValue & EZValue::operator[](size_t index)
{
	if (valueType_ != EZValueType::nullValue && valueType_ != EZValueType::arrayValue){
		throw std::runtime_error("operator[" + std::to_string(index) + "] value type error");
	}
	if (valueType_ == EZValueType::nullValue)
		*this = EZValueType(EZValueType::arrayValue);

	auto endidx = value_.get<ArrValue>().size();
	for (; endidx <= index; ++endidx){
		value_.get<ArrValue>().emplace_back(EZValue());
	}
	return value_.get<ArrValue>()[index];
}

inline const EZValue & EZValue::operator[](size_t index) const
{
	if (valueType_ != EZValueType::nullValue && valueType_ != EZValueType::arrayValue){
		throw std::runtime_error("operator[" + std::to_string(index) + "] value type error");
	}
	if (valueType_ == EZValueType::nullValue || index >= value_.get<ArrValue>().size())
		return null_value();
	return value_.get<ArrValue>()[index];
}

inline EZValue & EZValue::operator[](const std::string key)
{
	if (valueType_ != EZValueType::nullValue && valueType_ != EZValueType::objectValue){
		throw std::runtime_error("operator[" + key + "] value type error");
	}
	if (valueType_ == EZValueType::nullValue)
		*this = EZValueType(EZValueType::objectValue);
	return value_.get<ObjValue>()[key];
}

inline const EZValue & EZValue::operator[](const std::string key) const
{
	if (valueType_ != EZValueType::nullValue && valueType_ != EZValueType::objectValue){
		throw std::runtime_error("operator[" + key + "] value type error");
	}
	auto it = value_.get<ObjValue>().find(key);
	if (valueType_ == EZValueType::nullValue || it == value_.get<ObjValue>().end()){
		return null_value();
	}
    return it->second;
}

inline Members EZValue::memberNames() const
{
	Members fileds;
	if (valueType_ == EZValueType::objectValue)
	{
		for (auto& kv : value_.get<ObjValue>()){
			fileds.emplace_back(kv.first);
		}
	}
	return fileds;
}





